home *** CD-ROM | disk | FTP | other *** search
/ Sound Blastoff / Sound Blastoff.iso / Sound Utilities / Parrot10b1 Folder / source / Confusion.c next >
C/C++ Source or Header  |  1993-09-10  |  20KB  |  828 lines

  1. /**********
  2.  * Sound Confusion
  3.  *
  4.  *        Copyright © 1992 Bernie Bernstein
  5.  *        9/11/92 - 10/14/92
  6.  *
  7.  *        updated 9/4/93
  8.  ***********/
  9.  
  10.  
  11. #include    <GestaltEqu.h>
  12. #include    <Memory.h>
  13. #include    <OSEvents.h>
  14. #include    <Sound.h>
  15. #include    <SoundInput.h>
  16. #include    <StdIO.h>
  17. #include    <Types.h>
  18.  
  19. #include "Confusion.h"
  20.  
  21. /* how much memory should the program reserve after making the buffers? */
  22. #define kExtraMem        0x5000
  23.  
  24.  
  25. /* reset the channels every kResetInterval seconds */
  26. #define kResetInterval    30
  27.  
  28.  
  29. /*The HeaderSize constant is used to skip 20 bytes of the buffer when calling bufferCmd.
  30. The first 20 byte of the sound header need to be skipped so that the bufferCmd will be
  31. pointing at a SoundHeader Record and not an 'snd ' resource header. */
  32. #define kHeaderSize        20
  33.  
  34. typedef struct {
  35.     short        OnOff;
  36.     short         Level;
  37.     short         Length;
  38. } Level;
  39.  
  40.  
  41. Boolean    inStereo;
  42. short gThreshold;
  43. long gBuffSize;
  44. SCGlobals *globs;
  45. BufInfo gExtraBuffer;
  46. Boolean gPlayNRecord;
  47.  
  48. extern Boolean gQuit;
  49.  
  50. /***
  51.  * BufPlay
  52.  *        This routine takes an snd buffer and a sound channel and turns the information
  53.  *        into a bufferCmd to the channel.
  54.  ***/
  55. void BufPlay (Handle Buf, SndChannelPtr    channel)
  56. {
  57.     SndCommand        localSndCmd;
  58.     OSErr            err;
  59.     
  60.     localSndCmd.cmd = bufferCmd;
  61.     localSndCmd.param1 = 0;
  62.     localSndCmd.param2 = (long) ((*Buf) + kHeaderSize);
  63.     
  64.     err = SndDoCommand (channel, &localSndCmd, FALSE);
  65.     if (err != noErr)
  66.         AlertUser(err, iPlaying);
  67.     return;
  68. }
  69.  
  70.  
  71. /***
  72.  * SetUpSounds
  73.  *        SetUpSounds is a routine which takes a buffer handle, sound headers size and
  74.  *        sample rate value and turns it into a snd buffer with the proper header.
  75.  *        It then returns the buffer handle back to the caller.
  76.  ***/
  77. Handle SetUpSounds (Handle Buf, short *HeaderSize, Fixed sampRate)
  78. {
  79.     OSErr        err;
  80.     short        dummy;
  81.     
  82.     Buf = NewHandle(gBuffSize);
  83.     if ((err = MemError()) != noErr || Buf == nil)
  84.         AlertUser(err, iSetupBuffer);
  85.     HLockHi (Buf);
  86.     if ((err = MemError()) != noErr)
  87.         AlertUser(err, iSetupBuffer);
  88.     
  89.     /* The call to SetupSndHeader is done twice in order to make sure that the 
  90.     Header size is set correctly.  */
  91.  
  92.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, 0, HeaderSize);
  93.     if (err != noErr)
  94.         AlertUser(err, iSetupBuffer);
  95.     err = SetupSndHeader (Buf, 1, sampRate, 8, 'NONE', 0x3C, gBuffSize - *HeaderSize, &dummy);
  96.     if (err != noErr)
  97.         AlertUser(err, iSetupBuffer);
  98.         
  99.     return (Buf);
  100. }
  101.  
  102. /***
  103.  * RndRange
  104.  *        Pick a random number from min to max.
  105.  ***/
  106. short RndRange(short min, short max)
  107. {
  108.     unsigned short r = (unsigned short)Random();
  109.     short d = max-min+1;
  110.     return r / (0x10000/d) + min;
  111. }
  112.  
  113.  
  114. /***
  115.  * PickPlayableBuffer
  116.  *        Return the index of a buffer which is ready to be played. It randomly
  117.  *        picks one from the playable buffers, that is, ones which have some
  118.  *        sound in them. If it cant find one, then it returns a value too big.
  119.  ***/
  120. short PickPlayableBuffer(BufInfo *buffers, short max)
  121. {
  122.     short rnd;
  123.     short i;
  124.  
  125.     for(rnd = RndRange(0,max-1), i=0; 
  126.             !buffers[rnd].playable; 
  127.             i++)
  128.         {
  129.         /* try 100 times to find a good buffer before quitting */
  130.         /* oh shuddup, I know its dumb. */
  131.         if (i>100)
  132.             return max+1;
  133.  
  134.         rnd = RndRange(0,max-1);
  135.         }
  136.     return rnd;
  137. }
  138.  
  139.  
  140. /***
  141.  * PickReadableBuffer
  142.  *        Return the index of a buffer which can read some new sounds
  143.  *        First it fills up each buffer, then it randomly picks new ones
  144.  *        once they are all full.
  145.  ***/
  146. short PickReadableBuffer(BufInfo *buffers, short max)
  147. {
  148.     short rnd;
  149.     short i;
  150.  
  151.     for(i=0; i<max; i++)
  152.         {
  153.         if (!buffers[i].playable)
  154.             return i;
  155.         }
  156.  
  157.     return RndRange(0,max-1);
  158. }
  159.  
  160.  
  161. #define Abs(x)        ((x<0)?-(x):x)
  162. #define BlankSpace    0x1000
  163.  
  164. /***
  165.  * TrimBuffer
  166.  *        If the buffer is silent, then return FALSE. Otherwise, reduce the
  167.  *        buffer to just the parts that have sound.
  168.  ***/
  169. long TrimBuffer(Handle buffer, long headerlen)
  170. {
  171.     Boolean result = FALSE;
  172.     long oldBuffSize = GetHandleSize(buffer);
  173.     long oldEnd = 0;
  174.     long oldStart = headerlen;
  175.     long newIndex = 0;
  176.     long copySize = 0;
  177.     long bufByte = 0;
  178.     Boolean firstTime = TRUE;
  179.     Handle tmpbuffer = gExtraBuffer.buffer;
  180.  
  181.     /* copy header into new buffer */
  182.     BlockMove(*buffer, *tmpbuffer, headerlen);
  183.  
  184.     /* determine if the buffer was loud enough */
  185.     /* loud means that a byte in the buffer is far enough away from the */
  186.     /* center to get above the gThreshold */
  187.     for (bufByte=0; bufByte<oldBuffSize-headerlen; bufByte++)
  188.         {
  189.         short delta = 0x80 - (unsigned char)(*buffer)[headerlen + bufByte];
  190.         if (Abs(delta) << 1 > gThreshold)
  191.             {
  192.             /* the first time we hear something, save the previous BlankSpace bytes */
  193.             /* until now. */
  194.             if (firstTime)
  195.                 {
  196.                 /* If we are close to the beginning, copy from the beginning. */
  197.                 /* otherwise, copy blank space until this spot */
  198.                 if (bufByte > BlankSpace)
  199.                     oldStart = bufByte - BlankSpace;
  200.                 else
  201.                     oldStart = 0;
  202.                     
  203.                 /* If we are near the end, copy to the end. */
  204.                 /* otherwise, copy blank space after this spot */
  205.                 oldEnd = bufByte + BlankSpace;
  206.                 if (headerlen + oldEnd > oldBuffSize)
  207.                     oldEnd = oldBuffSize - headerlen;
  208.                 copySize = oldEnd - oldStart;
  209.  
  210.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  211.                 newIndex += copySize;
  212.                 bufByte = oldEnd;
  213.                 firstTime = FALSE;
  214.                 }
  215.             else
  216.                 {
  217.                 /* After the first time, bufByte > BlankSpace. */
  218.                 oldStart = bufByte - BlankSpace;
  219.                 if (oldStart < oldEnd)
  220.                     oldStart = oldEnd+1;
  221.  
  222.                 oldEnd = bufByte + BlankSpace;
  223.                 if (headerlen + oldEnd > oldBuffSize)
  224.                     oldEnd = oldBuffSize - headerlen;
  225.                 copySize = oldEnd - oldStart;
  226.  
  227.                 BlockMove(&(*buffer)[headerlen+oldStart], &(*tmpbuffer)[headerlen+newIndex], copySize);
  228.                 newIndex += copySize;
  229.                 bufByte = oldEnd;
  230.                 }
  231.             result = TRUE;
  232.             }
  233.         }
  234.     
  235.     if (result)
  236.         {
  237.         BlockMove(*tmpbuffer, *buffer, newIndex+headerlen);
  238.         return newIndex;
  239.         }
  240.     else
  241.         {
  242.         return 0L;
  243.         }
  244. }
  245.  
  246.  
  247.  
  248. #define TestBit(l,b)    ((l>>b)&0x01)
  249.  
  250. /***
  251.  * InitSoundConfusion
  252.  *        Allocate memory and initialize the sound driver
  253.  ***/
  254. void InitSoundConfusion(void)
  255. {
  256.     Level                myLevel;
  257.     OSErr                err;
  258.     long                feature;
  259.     BufInfo                *Buffers = nil;
  260.     short                i;
  261.     short                numBuffers;
  262.     short                meterState;
  263.     SPBPtr                RecordRec = nil;
  264.     Str255                settingStr;
  265.     long                tmplong;
  266.  
  267.     /* use Gestalt to make sure the app will work */
  268.     err = Gestalt(gestaltSoundAttr, &feature);
  269.     if (err == noErr)
  270.         {
  271.         if (!TestBit(feature, gestaltHasSoundInputDevice))
  272.             {
  273.             AlertUser(0, iNoInput);
  274.             ExitToShell();
  275.             }
  276.         else
  277.             {
  278.  
  279.             struct {
  280.                 short ignore;
  281.                 char majSys;
  282.                 char minSys;
  283.                 } sysVersion;
  284.  
  285.             /* does the machine have stereo? */
  286.             inStereo = TestBit(feature, gestaltStereoCapability);
  287.             
  288.             /* can the machine play and record simultaneously? */
  289.             /* prior to system 7.1, we could assume that if the machine */
  290.             /* had stereo, then it has the better Sound Chip and thus */
  291.             /* can play and record at the same time. 7.1 added the */
  292.             /* gestaltPlayAndRecord bit. */
  293.             err = Gestalt(gestaltSystemVersion, (long*)&sysVersion);
  294.             if (err != noErr)
  295.                 {
  296.                 AlertUser(err, iGestaltFailed);
  297.                 ExitToShell();
  298.                 }
  299.             gPlayNRecord = inStereo || TestBit(feature, gestaltPlayAndRecord);
  300.             }
  301.         }
  302.     else
  303.         {
  304.         AlertUser(err, iGestaltFailed);
  305.         ExitToShell();
  306.         }
  307.     
  308.     /* the threshold is a setting in the STR# resource */
  309.     GetIndString(settingStr, rSettingStrings, iThreshold);
  310.     StringToNum(settingStr, &tmplong);
  311.     gThreshold = tmplong;
  312.  
  313.     /* the buffer size is also kept as a string */
  314.     GetIndString(settingStr, rSettingStrings, iBufferSize);
  315.     StringToNum(settingStr, &tmplong);
  316.     gBuffSize = tmplong;
  317.     
  318.     /* There was a time when I was thinking of making an INIT, so I was */
  319.     /* going to keep all globals in a handle, but I changed my mind */
  320.     /* some of these can just be normal global variables, but what */
  321.     /* the heck. */
  322.     globs = (SCGlobals*)NewPtr(sizeof(SCGlobals));
  323.     if ((err = MemError()) != noErr || globs == nil)
  324.         {
  325.         AlertUser(err, iMakingGlobals);
  326.         return;
  327.         }
  328.     globs->fullBuffer = TRUE;
  329.     globs->loudEnough = FALSE;
  330.     globs->bufferGettingFilled = 0;
  331.     globs->leftChan = nil;
  332.     globs->rightChan = nil;
  333.     globs->buffers = nil;
  334.     globs->numBuffers = 0;
  335.     globs->RecordRec = nil;
  336.     globs->direction = iPlay;        /* next time through, it records */
  337.     
  338.     /* Open sound input drive (whichever one is selected in the sound cdev) */
  339.     err = SPBOpenDevice (nil, siWritePermission, &globs->SoundRefNum);
  340.     if (err != noErr)
  341.         {
  342.         AlertUser(err, iOpeningDevice);
  343.         return;
  344.         }
  345.  
  346.     /* Get the sample rate information for the snd header */
  347.     err = SPBGetDeviceInfo (globs->SoundRefNum,siSampleRate, (Ptr) &globs->sampleRate);
  348.     if (err != noErr)
  349.         {
  350.         AlertUser(err, iGettingRate);
  351.         return;
  352.         }
  353.     
  354.     /* build the RecordRec pointer and fill in the fields */
  355.     RecordRec = (SPBPtr) NewPtr(sizeof (SPB));
  356.     if ((err = MemError()) != noErr || RecordRec == nil)
  357.         {
  358.         AlertUser(err, iMakingRecordRec);
  359.         return;
  360.         }
  361.     globs->RecordRec = RecordRec;
  362.  
  363.     /* how many buffers can we make? (leave space for an extra buffer and then some memory) */
  364.     numBuffers = ((FreeMem() - kExtraMem) / gBuffSize) - 1;
  365.     if (numBuffers < 1)
  366.         {
  367.         AlertUser(0, iMemory);
  368.         return;
  369.         }
  370.     
  371.     Buffers = (BufInfo*)NewPtr(sizeof(BufInfo)*numBuffers);
  372.     if ((err = MemError()) != noErr || Buffers == nil)
  373.         {
  374.         AlertUser(err, iAllocatingBuffers);
  375.         return;
  376.         }
  377.     globs->buffers = Buffers;
  378.     globs->numBuffers = numBuffers;
  379.         
  380.     
  381.     /* build the snd buffers */
  382.     for (i=0; i<numBuffers; i++)
  383.         {
  384.         Buffers[i].buffer = SetUpSounds(Buffers[i].buffer, &Buffers[i].headerlength, globs->sampleRate);
  385.         Buffers[i].buffSize = gBuffSize - Buffers[i].headerlength;
  386.         Buffers[i].playable = FALSE;
  387.         }
  388.  
  389.     gExtraBuffer.buffer = SetUpSounds(gExtraBuffer.buffer, &gExtraBuffer.headerlength, globs->sampleRate);
  390.  
  391.     RecordRec->inRefNum = globs->SoundRefNum;
  392.     RecordRec->count =  gBuffSize - Buffers[0].headerlength;
  393.     RecordRec->milliseconds = 0;
  394.     RecordRec->bufferLength = gBuffSize - Buffers[0].headerlength;
  395.     RecordRec->bufferPtr = (Ptr) ((*Buffers[0].buffer) + Buffers[0].headerlength);
  396.     RecordRec->completionRoutine = (ProcPtr) &MyRecComp;
  397.     RecordRec->interruptRoutine = nil;
  398.     RecordRec->userLong = (long)globs;
  399.     RecordRec->error = 0;
  400.     RecordRec->unused1 = 0;
  401.     
  402.     globs->firstTime = TRUE;
  403.     
  404. }
  405.  
  406.  
  407. /***
  408.  * ResetChannels
  409.  *
  410.  *        Close (if not already) and then open the sound channel(s)
  411.  ***/
  412. void ResetChannels()
  413. {
  414.     SndCommand        mycmd;
  415.     OSErr            err;
  416.     static Boolean channelsExist = FALSE;
  417.     
  418.     if (channelsExist)
  419.         {
  420.         if (globs->leftChan)
  421.             {
  422.             /* kill the left channel (TRUE makes it shut up too) */
  423.             err = SndDisposeChannel (globs->leftChan, TRUE);
  424.             if (err != noErr)
  425.                 {
  426.                 AlertUser(err, iClosing);
  427.                 return;
  428.                 }
  429.             }
  430.         if (inStereo && globs->rightChan)
  431.             {
  432.             /* kill the right channel  (TRUE makes it shut up too) */
  433.             err = SndDisposeChannel (globs->rightChan, TRUE);
  434.             if (err != noErr)
  435.                 {
  436.                 AlertUser(err, iClosing);
  437.                 return;
  438.                 }
  439.             }
  440.         }
  441.  
  442.     /* if the machine has stereo, then open the right and left channels */
  443.     if (inStereo)
  444.         {
  445.         /* open the left channel which I can play from */
  446.         globs->leftChan = nil;
  447.         err = SndNewChannel (&globs->leftChan, sampledSynth, initChanLeft, nil);
  448.         if (err != noErr)
  449.             {
  450.             AlertUser(err, iMakingLeft);
  451.             return;
  452.             }
  453.         
  454.         /* open the right channel which I can play from */
  455.         globs->rightChan = nil;
  456.         err = SndNewChannel (&globs->rightChan, sampledSynth, initChanRight, nil);
  457.         if (err != noErr)
  458.             {
  459.             AlertUser(err, iMakingRight);
  460.             return;
  461.             }
  462.         }
  463.     else
  464.         {
  465.         /* open a mono channel and keep in the 'left' variable */
  466.         globs->leftChan = nil;
  467.         err = SndNewChannel (&globs->leftChan, sampledSynth, initMono, nil);
  468.         if (err != noErr)
  469.             {
  470.             AlertUser(err, iMakingMono);
  471.             return;
  472.             }
  473.         }
  474.  
  475.     channelsExist = TRUE;
  476. }
  477.  
  478.  
  479. /***
  480.  * ConfuseSound
  481.  *        This is called during idle time from the event loop.
  482.  *        It starts recording from a buffer and playing from another one.
  483.  ***/
  484. void ConfuseSound(void)
  485. {
  486.     short            dummy;
  487.     OSErr            err;
  488.     BufInfo            *Buffers;
  489.     short            i;
  490.     SndCommand        mycmd;
  491.     long            bufByte;
  492.     short            bufNum;
  493.     Boolean            stillWorking = FALSE;
  494.     Boolean            stillPlaying = FALSE;
  495.     Boolean            stillRecording = FALSE;
  496.     SCStatus        chanStatL;
  497.     SCStatus        chanStatR;
  498. #ifdef IFNEEDTORESETOCCASIONALLY
  499.     static    unsigned long    lastResetTime = 0L;
  500.     unsigned long    currTime;
  501.  
  502.     GetDateTime(&currTime);
  503.     
  504.     if (currTime - lastResetTime > kResetInterval)
  505.         {
  506.         ResetChannels();
  507.         lastResetTime = currTime;
  508.         if (gQuit)
  509.             return;
  510.         }
  511. #else
  512.     static Boolean    needToReset = TRUE;
  513.     /* only need to reset the first time, to setup the channels, etc */
  514.     if (needToReset)
  515.         {
  516.         ResetChannels();
  517.         needToReset = FALSE;
  518.         if (gQuit)
  519.             return;
  520.         }
  521. #endif
  522.  
  523.     /* easier access to the buffers */
  524.     Buffers = globs->buffers;
  525.  
  526.     /* some stuff to prevent us from playing and recording simultaneously */
  527.     /* on a machine that is incapable of it. */
  528.     if (globs->firstTime)
  529.         {
  530.         stillPlaying = FALSE;
  531.         stillRecording = FALSE;
  532.         }
  533.     else 
  534.         {
  535.         if (globs->direction == iPlay || gPlayNRecord)
  536.             {
  537.             err = SndChannelStatus(globs->leftChan, sizeof(SCStatus), &chanStatL);
  538.             if (err != noErr)
  539.                 {
  540.                 AlertUser(err, iPlaying);
  541.                 return;
  542.                 }
  543.             stillPlaying = chanStatL.scChannelBusy;
  544.  
  545.             if (inStereo && !stillPlaying)
  546.                 {
  547.                 err = SndChannelStatus(globs->rightChan, sizeof(SCStatus), &chanStatR);
  548.                 if (err != noErr)
  549.                     {
  550.                     AlertUser(err, iPlaying);
  551.                     return;
  552.                     }
  553.                 stillPlaying = chanStatR.scChannelBusy;
  554.                 }
  555.                 
  556.             }
  557.         if (globs->direction == iRecord || gPlayNRecord)
  558.             {
  559.             stillRecording = !globs->fullBuffer;
  560.             }
  561.         }
  562.     
  563.     stillWorking = stillPlaying || stillRecording;
  564.     
  565.     /* if we cant play and record, then exit if we are in the middle of */
  566.     /* playing or recording. we can pass through as soon as the process is done */
  567.     if ((!gPlayNRecord && stillWorking) || (stillPlaying && stillRecording))
  568.         return;
  569.  
  570.  
  571.     /* if this is the first time through, then prepare to record into a buffer */
  572.     /* if a buffer recording has finished (ie, the buffer is full), then prepare */
  573.     /* to record into another buffer */
  574.     if (!stillRecording)
  575.         {
  576.         globs->fullBuffer = FALSE;
  577.     
  578.         if (!globs->firstTime && (gPlayNRecord || globs->direction == iRecord))
  579.             {
  580.             long newSize;
  581.             bufNum = globs->bufferGettingFilled;
  582.             
  583.             /* trim the buffer by removing silent (quiet) sections */
  584.             newSize = TrimBuffer(Buffers[bufNum].buffer, Buffers[bufNum].headerlength);
  585.             Buffers[bufNum].playable = globs->loudEnough = (newSize > 0);
  586.             Buffers[bufNum].buffSize = newSize;
  587.     
  588.             /* if the buffer was loud enough, then prepare to record the next readable buffer */
  589.             if (globs->loudEnough)
  590.                 {
  591.                 short nextbuf = PickReadableBuffer(Buffers, globs->numBuffers);
  592.                 globs->bufferGettingFilled = nextbuf;
  593.                 globs->loudEnough = FALSE;
  594.                 Buffers[nextbuf].playable = FALSE;
  595.                 HUnlock(Buffers[nextbuf].buffer);
  596.                 SetHandleSize(Buffers[nextbuf].buffer, gBuffSize);
  597.                 HLockHi(Buffers[nextbuf].buffer);
  598.                 globs->RecordRec->bufferPtr = (*Buffers[nextbuf].buffer) + Buffers[nextbuf].headerlength;
  599.                 }
  600.             }
  601.                 
  602.         globs->firstTime = FALSE;
  603.  
  604.         if (gPlayNRecord)
  605.             {
  606.             /* start recording into the buffer asynchronously */
  607.             err = SPBRecord (globs->RecordRec, TRUE); // start recording
  608.             if (err != noErr)
  609.                 {
  610.                 AlertUser(err, iRecording);
  611.                 return;
  612.                 }
  613.             }
  614.         }
  615.     
  616.     if (gPlayNRecord && !stillPlaying)
  617.         {
  618.         /* pick a buffer to play from */    
  619.         i = PickPlayableBuffer(Buffers, globs->numBuffers);
  620.         if (i<=globs->numBuffers)
  621.             {
  622.             long bsize = Buffers[i].buffSize;
  623.             err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
  624.             if (err != noErr)
  625.                 {
  626.                 AlertUser(err, iPlaying);
  627.                 return;
  628.                 }
  629.             /* play it asynchronously */
  630.             BufPlay(Buffers[i].buffer, 
  631.                     inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
  632.             }
  633.         }
  634.     
  635.     
  636.     /* if we cant play and record simultaneously, then do the one that */
  637.     /* we werent doing last. in other words, take turns playing and recording */
  638.     if (!gPlayNRecord)
  639.         {
  640.         if (globs->direction == iPlay)
  641.             {
  642.             globs->fullBuffer = FALSE;
  643.             /* start recording into the buffer asynchronously */
  644.             err = SPBRecord (globs->RecordRec, TRUE); // start recording
  645.             if (err != noErr)
  646.                 {
  647.                 AlertUser(err, iRecording);
  648.                 return;
  649.                 }
  650.             globs->direction = iRecord;
  651.             }
  652.         else /* if (globs->direction == iRecord) */
  653.             {
  654.             /* pick a buffer to play from */    
  655.             i = PickPlayableBuffer(Buffers, globs->numBuffers);
  656.             if (i<=globs->numBuffers)
  657.                 {
  658.                 long bsize = Buffers[i].buffSize;
  659.                 err = SetupSndHeader (Buffers[i].buffer, 1, globs->sampleRate, 8, 'NONE', 0x3C, bsize, &dummy);
  660.                 if (err != noErr)
  661.                     {
  662.                     AlertUser(err, iPlaying);
  663.                     return;
  664.                     }
  665.                 /* play it asynchronously */
  666.                 BufPlay(Buffers[i].buffer, 
  667.                         inStereo ? (RndRange(0,1) ? globs->leftChan : globs->rightChan) : globs->leftChan);
  668.                 }
  669.             globs->direction = iPlay;
  670.             globs->fullBuffer = TRUE;
  671.             }
  672.         }
  673. }
  674.  
  675.  
  676. /***
  677.  * StopConfusion
  678.  *        Deallocate memory and close stuff.
  679.  ***/
  680. void StopConfusion(void)
  681. {
  682.     OSErr            err;
  683.     SndCommand        mycmd;
  684.     short            i;
  685.  
  686.     if (!globs)
  687.         return;
  688.  
  689.     if (globs->leftChan)
  690.         {
  691.         /* kill the left channel */
  692.         err = SndDisposeChannel (globs->leftChan, TRUE);
  693.         if (err != noErr)
  694.             AlertUser(err, iClosing);
  695.         }
  696.     if (inStereo && globs->rightChan)
  697.         {
  698.         /* kill the right channel */
  699.         err = SndDisposeChannel (globs->rightChan, TRUE);
  700.         if (err != noErr)
  701.             AlertUser(err, iClosing);
  702.         }
  703.  
  704.     if (globs->SoundRefNum != 0)
  705.         {
  706.         /* stop recording and close the input device */
  707.         err = SPBStopRecording(globs->SoundRefNum);
  708.         if (err != noErr)
  709.             AlertUser(err, iClosing);
  710.         err = SPBCloseDevice (globs->SoundRefNum);
  711.         if (err != noErr)
  712.             AlertUser(err, iClosing);
  713.         }
  714.     
  715.     if (globs->buffers)
  716.         {
  717.         /* kill the snd buffers */
  718.         for (i=0; i<globs->numBuffers; i++)
  719.             {
  720.             if (globs->buffers[i].buffer)
  721.                 {
  722.                 HUnlock(globs->buffers[i].buffer);
  723.                 DisposeHandle(globs->buffers[i].buffer);
  724.                 }
  725.             }
  726.         }
  727.  
  728.     if (gExtraBuffer.buffer)
  729.         {
  730.         HUnlock(gExtraBuffer.buffer);
  731.         DisposeHandle(gExtraBuffer.buffer);
  732.         }
  733.  
  734.     /* kill the buffers */
  735.     DisposePtr ((Ptr) globs->buffers);
  736.     DisposePtr ((Ptr) globs->RecordRec);
  737.     DisposePtr ((Ptr) globs);
  738. }
  739.  
  740.  
  741. /**********************************/
  742.  
  743. void DoAbout()
  744. {
  745.     Str31 numChannelsStr;
  746.     Str31 maxCPUStr;
  747.     Str31 curCPUStr;
  748.     SMStatus stat;
  749.  
  750.     OSErr err;
  751.     Str31 numBufsStr;
  752.     Str31 numFilledStr;
  753.     short numFilled = 0;
  754.     short i;
  755.     short itemHit;
  756.     DialogPtr dlg;
  757.     
  758.     err = SndManagerStatus(sizeof(SMStatus), &stat);
  759.     if (err != noErr)
  760.         {
  761.         AlertUser(err, iGettingSMStatus);
  762.         }
  763.     NumToString((long)stat.smNumChannels, numChannelsStr);
  764.     NumToString((long)stat.smMaxCPULoad, maxCPUStr);
  765.     NumToString((long)stat.smCurCPULoad, curCPUStr);
  766.     Pstrcat(curCPUStr, "\p/");
  767.     Pstrcat(curCPUStr, maxCPUStr);
  768.     
  769.     for (i=0; i<globs->numBuffers; i++)
  770.         {
  771.         if (globs->buffers[i].playable)
  772.             numFilled++;
  773.         }
  774.     NumToString((long)globs->numBuffers, numBufsStr);
  775.     NumToString((long)numFilled, numFilledStr);
  776.     ParamText(numBufsStr, numFilledStr, numChannelsStr, curCPUStr);
  777.  
  778.     dlg = GetNewDialog(rAboutAlert, NULL, (WindowPtr)-1L);
  779.     ModalDialog(NULL, &itemHit);
  780.     DisposDialog(dlg);
  781. }
  782.  
  783.  
  784. /***
  785.  * MyRecComp
  786.  *
  787.  * This is the Completion Routine which is called every time the buffer,
  788.  * which is being recorded into, is full.
  789.  ***/
  790. pascal void MyRecComp (SPBPtr inParamPtr)
  791. {
  792.     SCGlobals *glob;
  793.     
  794.     glob = (SCGlobals*)inParamPtr->userLong;
  795.     glob->fullBuffer = TRUE;
  796.  
  797.     if (inParamPtr->error != noErr && !gQuit)
  798.         {
  799.         AlertUser(inParamPtr->error, iPlaying);
  800.         }
  801.  
  802.     return;
  803. }
  804.  
  805.  
  806. /***
  807.  * Pstrcat
  808.  *
  809.  *        Concat two pascal strings
  810.  ***/
  811. unsigned char *
  812. Pstrcat(
  813.     register Str255            s1,
  814.     register Str255            s2 )
  815. {
  816.     register unsigned short    i;
  817.     unsigned short        l = s2[0];
  818.     unsigned char        *s = s1;
  819.     
  820.     s1 = &s1[s1[0]];
  821.     for( i = 1; i <= l; i++ ) {
  822.         *++s1 = *++s2;
  823.     }
  824.     s[0] += --i;
  825.     
  826.     return( s );
  827. }
  828.